package com.cwg.dict.stem.service.impl;

import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cwg.dict.stem.model.Group;
import com.cwg.dict.stem.model.Section;
import com.cwg.dict.stem.model.Sections;
import com.cwg.dict.stem.model.StemType;
import com.cwg.dict.stem.model.Token;
import com.cwg.dict.stem.service.WordParser;

public class WordParserImpl implements WordParser {

	private Logger		logger	= LoggerFactory.getLogger(WordParserImpl.class);
	private Sections	sections;

	public Sections getSections() {
		return sections;
	}

	@Override
	public Token[] query(String input) {
		if (sections == null) {
			try {
				sections = loadSections();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (JAXBException e) {
				e.printStackTrace();
			}
		}

		List<Token> results = new ArrayList<Token>();
		results.addAll(Arrays.asList(getPrefixes(input)));
		results.addAll(Arrays.asList(getStems(input)));
		results.addAll(Arrays.asList(getSuffixes(input)));
		return results.toArray(new Token[results.size()]);
	}

	public void setSections(Sections sections) {
		this.sections = sections;
	}

	private Token[] queryToken(String input, StemType stem) {
		Set<Token> results = new LinkedHashSet<Token>();
		for (Section section : sections.getSections()) {
			if (stem != section.getCategory()) {
				continue;
			}
			for (Group group : section.getGroups()) {
				List<Token> tokens = group.getTokens();
				for (Token token : tokens) {
					Set<Token> splitToken = splitToken(token, input);
					results.addAll(splitToken);
				}
			}
		}
		return results.toArray(new Token[results.size()]);
	}

	private Set<Token> splitToken(Token token, String input) {
		Set<Token> results = new LinkedHashSet<Token>();
		logger.debug("processing tokens : " + token.getName());
		String[] names = StringUtils.split(token.getName(), ",");
		for (String name : names) {
			logger.debug("processing token : " + name);
			if (StringUtils.contains(input.toLowerCase(), name.trim().toLowerCase())) {
				logger.debug("add stem to results : " + token.getName());
				results.add(token);
			}
		}
		return results;
	}

	protected Token[] getPrefixes(String input) {
		return queryToken(input, StemType.PREFIX);
	}

	protected Token[] getStems(String input) {
		return queryToken(input, StemType.STEM);
	}

	protected Token[] getSuffixes(String input) {
		return queryToken(input, StemType.SUFFIX);
	}

	protected Sections loadSections() throws FileNotFoundException, JAXBException {
		InputStream stream = WordParserImpl.class.getResourceAsStream("/stem.xml");
		if (stream == null) {
			throw new FileNotFoundException("/stem.xml");
		}
		JAXBContext context2 = JAXBContext.newInstance(Sections.class);
		Unmarshaller um = context2.createUnmarshaller();
		Sections results = (Sections) um.unmarshal(stream);
		/*
		 * NOTICE : the parent element will not be assigned after jaxb un-marshaled  
		 * For bi-direction navigate the model (parent <--> children), we need set the parent back to child
		 * for example:
		 * it could navigate from section -> group -> token, but not section -> group -> token
		 *  after added the parent relation to children, it could navigate from parent to children or vice-versa 
		 *  the model become as  section <-> group <-> token
		 *   
		 */
		for (Section section : results.getSections()) {
			List<Group> groups = section.getGroups();
			for (Group group : groups) {
				group.setSection(section);
				List<Token> tokens = group.getTokens();
				for (Token token : tokens) {
					token.setGroup(group);
				}
			}
		}
		return results;
	}
}
